#include "des.h"
#include <math.h>
#include <boost/lexical_cast.hpp>
#include <sys/timeb.h>
#include <fstream>
#include <cstring>
#include <cassert>
#include <cstdio>

const int N_NETWORK_TYPES = 8; // 8 different networks

using namespace std;

DES::DES (Param p)  : 
    actors(0), 
    socialNetwork(0), 
    simParam(p), 
    udist(0, 1.0),
    urandom(generator, udist)
{
    this->init(p);
}

void DES::init(Param p)
{
    simParam = p;
    if (actors != 0) {
        delete [] actors;
    }
    actors = new Actor [p.N];

    if(socialNetwork != 0) {
        delete socialNetwork;
    }
    socialNetwork = new Network(simParam.N, simParam.density);

    for (int i = 0; i < simParam.N; i++) {
        actors[i].init(i, socialNetwork);
    }
}

DES::~DES()
{
    delete [] actors;
    delete socialNetwork;
}

void DES :: execute(
    const char *filename,
    int dump
)
{
    char fSummary[80], fLog[80], fNetwork[120];

    strcpy(fSummary, filename);
    strcat(fSummary, ".summary");
    strcpy(fLog, filename);
    strcat(fLog, ".log");

    ofstream ofileSummary(fSummary, ios_base::app);
    ofstream ofileLog(fLog, ios_base::app);

    assert(ofileSummary.good());
    assert(ofileLog.good());

    double mean_acts = 0;
    double mean_iterations = 0;
    double mean_utility = 0;

    int networkHist[NS_TYPE_SIZE] = {0, 0, 0, 0, 0, 0, 0, 0};

    // Dump Input parameters in csv format in both log and summary
    // files
    ofileLog << simParam.random_seed << ", " << simParam.N << ", "
        << simParam.expt << ", " << simParam.n_repeat << ", "
        << simParam.c << ", " << simParam.delta << ", "
        << simParam.density << endl;

    ofileSummary << simParam.random_seed << ", " << simParam.N << ", "
        << simParam.expt << ", " << simParam.n_repeat << ", "
        << simParam.c << ", " << simParam.delta << ", "
        << simParam.density << ", ";


    int *schedule = new int [simParam.N];
    int done = 0;
    int idle = simParam.n_idle_terminate;

    // Repeat the experiment n times
    for (int n = 0; n < simParam.n_repeat; n++) {

        // Run the experiment once upto maxInterations
        int i = 0;
        for (i = 0; i < simParam.n_iterations; i++) {

            // Dump the graph at an appropriate time
            if ((dump >=  1) && (i % dump == 0)) {
                sprintf(fNetwork, "snstruct_%05d_%05d.dot", n, i);
                socialNetwork->dumpNetwork(fNetwork);
            }

            // Create a random schedule
            randomSchedule(schedule, simParam.N);

            // Implement the schedule
            done = 0;
            for (int k = 0; k < simParam.N; k++) {
                done += actors[schedule[k]].act();
            }

            // update the statistics for the number of acts and time
            mean_acts += done;
            mean_iterations += 1.0;

            // If no actor updated the social network
            if (done == 0) {
                idle--;
                if (idle <= 0) {
                    // and for nIdleTerminate times
                    break;
                }
            } else {
                idle = simParam.n_idle_terminate;
            }
        } // n_iterations

        // update the mean utility
        for (int i = 0; i < simParam.N; i++) {
            mean_utility +=  actors[i].getUtility(socialNetwork);
        }

        // Get the network type
        networkHist[(unsigned) socialNetwork->getNetworkStructure(&ofileLog)]++;
        init(simParam);
    }
    delete [] schedule ;

    // Compute the actual means
    mean_utility = mean_utility/simParam.n_repeat;
    mean_acts = mean_acts/simParam.n_repeat;
    mean_iterations = mean_iterations/simParam.n_repeat;

    ofileSummary << mean_acts << ", " << mean_iterations << ", "
        << mean_utility;
    for (int j = 0; j < N_NETWORK_TYPES; j++) {
        ofileSummary << ", " << networkHist[j];
    }
    ofileSummary << endl;

    ofileLog.close();
    ofileSummary.close();
}

void DES::test()
{
    Network startGraph(10, 0.6);
    Network startGraph1(startGraph);

    cout << startGraph1.getNumEdgesOnGeodesic(1, 3)<< endl;
    cout << startGraph1.isAdjacent(1, 4) << endl;
    cout << startGraph1.isAdjacent(1, 5)<< endl;
    startGraph1.add( 1, 4);
    cout << startGraph1.isAdjacent(1, 5) << endl;
    startGraph1.remove( 1, 4);
    cout << startGraph1.isAdjacent(1, 4) << endl;
    cout << startGraph1.getNetworkStructure(NULL) << endl;

    timeb test;
    ftime(&test);
    string fileName_ROOT = "/home/rohithdv/rohith/COURSE_WORK_JAN2010/GAME_THEORY_Y_NARAHARI/Miniproject/HammonImpl/forsonet/";

    string fileNameSecs = boost::lexical_cast<string>(test.time);
    string fileNameMillis = boost::lexical_cast<string>(test.millitm);
    string fileNameExtn = ".dot";
    string fileName = "";

    fileName.append(fileName_ROOT);
    fileName.append(fileNameSecs);
    fileName.append("_");
    fileName.append(fileNameMillis);
    fileName.append(fileNameExtn);

    startGraph.dumpNetwork(fileName.c_str());
    return;
}

void DES::writeMetrics (const char* fileName)
{
    fileName = fileName;
}

int DES :: randint(int l, int u)
{
    double r  = urandom();
    return ( int(r*(u - l + 1.)) + l);
}

void DES :: swap(int &p, int &q)
{
    int tmp = p;
    p = q;
    q = tmp;
}

void DES :: randomSchedule(int schedule[], int N)
{
    for (int i = 0; i < N; i++) {
        schedule[i] = i;
    }

    for (int i = 0; i < N; i++) {
        swap(schedule[i], schedule[randint(i, N-1)]);
    }
}
